﻿using System;
using System.Collections.Generic;
using System.Data;
using MedicService.Domain.Generic;

namespace MedicService.Server.Factory.Generic {
    public abstract class EntityFactory<TEntity> where TEntity : Entity {

        readonly protected string TableName;
        readonly protected string OrderBy;
        readonly protected string Where;

        protected Dictionary<int, TEntity> ItemsMap;

        protected EntityFactory(string tableName, string orderBy, string where) {
            OrderBy = orderBy;
            TableName = tableName;
            Where = where;
        }

        protected EntityFactory(string tableName, string orderBy) {
            TableName = tableName;
            OrderBy = orderBy;
            Where = null;
            //ItemsMap = GetMap();
        }

        protected EntityFactory(string tableName) {
            TableName = tableName;
            OrderBy = "id";
            Where = null;
            //ItemsMap = GetMap();
        }

        public void InitMap() {
            ItemsMap = GetMap(true);
        }

        public List<TEntity> GetList(bool useDeleted) {
            var entities = new List<TEntity>();
            var dataTable = DatabaseConnector.GetData(TableName, OrderBy, Where);
            var deleted = DeletedFactory.getByType<TEntity>();
            foreach (DataRow myDataRow in dataTable.Rows) {
                var entity = CreateEntity();
                FillEntity(entity, myDataRow);
                if (useDeleted || !deleted.Contains(entity.Id)) {
                    entities.Add(entity);
                }
            }
            return entities;
        }

        public Dictionary<int, TEntity> GetMap(bool useDeleted) {
            var entities = new Dictionary<int, TEntity>();
            var list = GetList(useDeleted);
            foreach (var entity in list) {
                entities.Add(entity.Id, entity);
            }
            entities[0] = CreateEntity();
            return entities;
        }

        protected virtual void FillEntity(TEntity entity, DataRow myDataRow) {
            entity.Id = (int)myDataRow["id"];
        }

        protected abstract TEntity CreateEntity();

        public virtual void Save(TEntity entity) {
            if (entity.Id <= 0) {
                var query = GetInsertQuery(entity);
                query += ";SELECT SCOPE_IDENTITY();";
                var insert = DatabaseConnector.Insert(query);
                entity.Id = insert;
            } else {
                var query = GetUpdateQuery(entity);
                DatabaseConnector.Update(query);
            }
        }

        //public TEntity Get(object o) {
        //    if (o == null) {
        //        return CreateEntity();
        //    }else {
        //        string s = o.ToString();
        //        if (s.Length == 0) {
        //            return CreateEntity();
        //        }else {
        //            int id = Convert.ToInt32(s);
        //            return Get(id);
        //        }
        //    }
        //}

        //public TEntity Get(int id) {
        //    if (ItemsMap.ContainsKey(id)) {
        //        return ItemsMap[id];
        //    }
        //    return CreateEntity();
        //}

        public TEntity Load(object id) {
            if (id == null) {
                return CreateEntity();
            } else {
                return Load(id.ToString());
            }
        }

        public TEntity Load(string id) {
            if (id.Length == 0) {
                return CreateEntity();
            } else {
                return Load(int.Parse(id));
            }
        }

        public TEntity Load(int id) {
            if (ItemsMap == null) {
                DataRow dataRow = DatabaseConnector.GetById(TableName, id);
                TEntity entity = CreateEntity();
                if (dataRow != null) {
                    FillEntity(entity, dataRow);
                }
                return entity;
            } else {
                if (ItemsMap.ContainsKey(id)) {
                    return ItemsMap[id];
                } else {
                    return CreateEntity();
                }
            }
        }

        protected abstract string GetInsertQuery(TEntity entity);

        protected abstract string GetUpdateQuery(TEntity entity);


        protected static string Date(DateTime dateTime) {
            return "'" + dateTime.Year + X2(dateTime.Month) + X2(dateTime.Day) + "'";
        }

        protected static string Date(DateTime? dateTime) {
            if (dateTime != null) {
                return "'" + dateTime.Value.Year + X2(dateTime.Value.Month) + X2(dateTime.Value.Day) + "'";
            }else {
                return "NULL";
            }
        }

        protected static string DateTime(DateTime dateTime) {
            return "'" + dateTime.Year + X2(dateTime.Month) + X2(dateTime.Day) +
                   " " + X2(dateTime.Hour) + ":" + X2(dateTime.Minute) + ":" + X2(dateTime.Second) + ".000'";
        }

        private static string X2(int i) {
            if (0 <= i && i < 10) {
                return "0" + i;
            } else {
                return "" + i;
            }
        }

        protected static string Nullable(Guid guid) {
            if (Guid.Empty == guid) {
                return "NULL";
            }else {
                return "'" + guid.ToString() + "'";
            }
        }

        public static object Nullable(Entity item) {
            if (item == null || item.Id == 0) {
                return "NULL";
            } else {
                return "'" + item.Id + "'";
            }
        }

        protected static string NotNullString(string s) {
            return s == null ? "''" : "'" + s + "'";
        }

        protected static string Nullable(object o) {
            if (o == null) {
                return "NULL";
            } else {
                string s = "" + o;
                if (string.IsNullOrEmpty(s)) {
                    return "NULL";
                } else {
                    return "'" + s + "'";
                }
            }
        }

        protected static string Nullable<T>(List<T> entities) where T : Entity {
            string result = "";
            foreach (var entity in entities) {
                result += "(" + entity.Id + ")";
            }
            return result.Length > 0 ? "'" + result + "'" : "NULL";
        }

        protected static List<int> ParseIdList(string list) {
            var ids = new List<int>();
            if (list != null) {
                string[] strings = list.Replace(")", "").Split('(');
                for (int i = 1; i < strings.Length; i++) {
                    ids.Add(Convert.ToInt32(strings[i]));
                }
            }
            return ids;
        }

    }

}